package controller

import (
	"encoding/base64"
	"fmt"
	"github.com/aliyun/aliyun-oss-go-sdk/oss"
	"github.com/mjiulee/lego"
	"github.com/mjiulee/lego/utils"
	"github.com/valyala/fasthttp"
	"io"
	"io/ioutil"
	"mime/multipart"
	"os"
	"path"
	"path/filepath"
	"saas_sys_base/sysconfig"
	"strings"
	"time"
)

type UploadCtrl struct {
	savePath string
}

func init() {
	vc := new(UploadCtrl)
	vc.savePath = "upload"
	_ctrlList = append(_ctrlList, vc)
}

func (vc *UploadCtrl) CtrlInfo() string {
	return "UploadCtrl"
}

func (vc *UploadCtrl) RoutInit() {
	// 列表页面
	lego.POST("/admin/upload", vc.upload, lego.LEGO_SESSION_TYPE_ADMIN,false)
	lego.POST("/admin/uploadalioss", vc.UploadAliOss, lego.LEGO_SESSION_TYPE_ADMIN,false)
	lego.POST("/admin/uploadb64", vc.UploadBase64, lego.LEGO_SESSION_TYPE_ADMIN,false)
	// ueditor特殊处理，做编辑器上传图片处理
	lego.GET("/ueditor/config", vc.GetEditorconfig, lego.LEGO_SESSION_TYPE_ADMIN,false)
	lego.GET("/ueditor/upload", vc.GetEditorUpload, lego.LEGO_SESSION_TYPE_ADMIN,false)
	lego.POST("/ueditor/upload", vc.PostEditorUploadAliOss, lego.LEGO_SESSION_TYPE_ADMIN,false)
	lego.POST("/ueditor/uploadalioss", vc.PostEditorUploadAliOss, lego.LEGO_SESSION_TYPE_ADMIN,false)
}

/** 处理文件上传
 * params
 */
func (t *UploadCtrl) upload(ctx *fasthttp.RequestCtx) {
	// 处理上传到本地服务器
	savePath, _, err := t.handelFile(ctx)
	if err != nil {
		ctx.SetStatusCode(500)
		return
	}

	// 一切ok，返回数据
	reponse := make(map[string]interface{})
	code := 0
	msg := "成功"

	reponse["code"] = code
	reponse["msg"] = msg

	data := make(map[string]interface{})
	data["imgurl"] = "/static" + savePath

	reponse["data"] = data

	ctxExt := lego.RequestCtxExtent{ctx}
	ctxExt.JSON(200, reponse)
	return
}

/** 处理文件上传
 * params
 */
func (t *UploadCtrl) UploadAliOss(ctx *fasthttp.RequestCtx) {
	ctxExt := lego.RequestCtxExtent{ctx}
	//1. 处理上传到本地服务器
	/*_, filename, err := t.handelFile(ctx)
	if err != nil {
		ctx.SetStatusCode(500)
		return
	}*/

	fh,filename, err := t.OpenUploadFile(ctx, "file")
	if err != nil {
		ctx.SetStatusCode(500)
		return
	}
	defer fh.Close()

	//2. 上传到oss服务器
	prjname := sysconfig.Config.Code.Prjname //lego.GetIniByKey("CODE", "PRJNAME")
	endpoint := sysconfig.Config.Aliyun_Oss.Endpoint //lego.GetIniByKey("ALIYUN-OSS", "ENDPOINT")
	accessKeyId := sysconfig.Config.Aliyun_Oss.Key_Id //lego.GetIniByKey("ALIYUN-OSS", "KEYID")
	accessKeySecret := sysconfig.Config.Aliyun_Oss.Key_Secret //lego.GetIniByKey("ALIYUN-OSS", "KEYSECRET")
	bucketName := sysconfig.Config.Aliyun_Oss.Bucket //lego.GetIniByKey("ALIYUN-OSS", "BUCKET")
	objectName := prjname + "/" + filename

	// 创建OSSClient实例。
	client, err := oss.New(endpoint, accessKeyId, accessKeySecret)
	if err != nil {
		lego.LogError(err)
		ctx.SetStatusCode(500)
		return
	}

	// 获取存储空间。
	bucket, err := client.Bucket(bucketName)
	if err != nil {
		lego.LogError(err)
		ctx.SetStatusCode(500)
		return
	}

	//localFileName = strings.ReplaceAll(localFileName,"\\", "/")
	objectName = strings.ReplaceAll(objectName,"\\", "/")
	// 上传文件。
	err = bucket.PutObject(objectName, fh) //err = bucket.PutObjectFromFile(objectName, localFileName)
	if err != nil {
		lego.LogError(err)
		ctx.SetStatusCode(500)
		return
	}

	//3. 返回给客户端
	reponse := make(map[string]interface{})
	code := 0
	msg := "成功"

	reponse["code"] = code
	reponse["msg"] = msg

	data := make(map[string]interface{})
	data["imgurl"] = "http://" + bucketName + "." + endpoint + "/" + objectName

	reponse["data"] = data


	ctxExt.JSON(200, reponse)
	return
}

/** 处理文件上传base64图片文件
 * params
 */
func (t *UploadCtrl) UploadBase64(ctx *fasthttp.RequestCtx) {
	ctxExt := lego.RequestCtxExtent{ctx}
	code := 0
	msg := "success"

	response := make(map[string]interface{})
	data := make(map[string]interface{})
	for {
		datasource := string(ctx.FormValue("base64img"))
		if len(datasource) <= 0 {
			code = 100
			msg = "miss data"
			break
		}
		// fmt.Println(datasource)
		ddd, err1 := base64.StdEncoding.DecodeString(datasource) //成图片文件并把文件写入到buffer
		if err1 != nil {
			code = 100
			msg = "err data"
			break
		}
		/* 按照时间保存 */
		timestamp := string(utils.RandString(32, 3))
		// 保存路径
		savePath := t.savePath + "/" + timestamp + ".jpg"
		lego.LogInfo("savePath=" + savePath)
		err2 := ioutil.WriteFile(savePath, ddd, 0666) //buffer输出到jpg文件中（不做处理，直接写到文件）
		if err2 != nil {
			code = 100
			msg = "file write err"
			break
		}

		data["imgurl"] = "/static/" + timestamp + ".jpg"
		response["data"] = data
		break
	}

	response["code"] = code
	response["msg"] = msg
	ctxExt.JSON(200, response)
}

/* 处理文件保存相关逻辑 */
func (t *UploadCtrl) handelFile(ctx *fasthttp.RequestCtx) (spath, fname string, err error) {
	// 这里直接获取到 multipart.FileHeader, 需要手动打开文件句柄
	f, err := ctx.FormFile("file")
	if err != nil {
		lego.LogError(err)
		return "", "", err
	}

	fh, err := f.Open()
	if err != nil {
		lego.LogError(err)
		return "", "", err
	}
	defer fh.Close()

	// 按照时间保存
	now := time.Now().Format("20060102")
	timestamp := string(utils.RandString(32, 3))
	saveDir := filepath.Join(t.savePath, path.Clean("/"+now))
	if exist, _ := utils.PathExists(saveDir); !exist {
		mkdirerr := os.Mkdir(saveDir, os.ModePerm)
		if mkdirerr != nil {
			lego.LogError(mkdirerr)
			return "", "", err
		}
	}

	// 保存路径
	savePath := path.Clean("/" + now + "/" + timestamp + path.Ext(f.Filename))
	lego.LogInfo("savePath=" + savePath)
	// 打开保存文件句柄
	fileName := filepath.Join(t.savePath, savePath)
	fp, err := os.OpenFile(fileName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
	if err != nil {
		lego.LogError(err)
		return "", "", err
	}
	defer fp.Close()

	if _, err = io.Copy(fp, fh); err != nil {
		lego.LogError(err)
		return "", "", err
	}

	return savePath, fileName, nil
}

/* 处理文件保存相关逻辑
代码示例
fh,filename, err := t.OpenUploadFile(ctx, "file")
if err != nil {
	ctx.SetStatusCode(500)
	return
}
defer fh.Close()
 */
func (t *UploadCtrl) OpenUploadFile(ctx *fasthttp.RequestCtx, upfilename string) (upfileFile multipart.File, filename string, err error) {
	// 这里直接获取到 multipart.FileHeader, 需要手动打开文件句柄
	f, err := ctx.FormFile(upfilename)
	if err != nil {
		lego.LogError(err)
		return nil, "", err
	}

	fh, err := f.Open()
	if err != nil {
		lego.LogError(err)
		return nil, "", err
	}

	//调用这个函数后，需要关闭fh
	//defer fh.Close()
	//生成upload存放文件的目录，可以用于oss中的存放路径
	now := time.Now().Format("20060102")
	timestamp := string(utils.RandString(32, 3))

	savePath := path.Clean("/" + now + "/" + timestamp + path.Ext(f.Filename))
	//lego.LogInfo("savePath=" + savePath)
	// 打开保存文件句柄
	fileName := filepath.Join(t.savePath, savePath)

	return fh, fileName, nil
}

/** 百度图文Editor处理文件上传
 * params
*/
func (vc *UploadCtrl) PostEditorUploadAliOss(ctx *fasthttp.RequestCtx) {
	fh,filename, err := vc.OpenUploadFile(ctx, "upfile")
	if err != nil {
		lego.LogError(err)
		ctx.SetStatusCode(500)
		return
	}
	defer fh.Close()

	//2. 上传到oss服务器
	prjname := sysconfig.Config.Code.Prjname //lego.GetIniByKey("CODE", "PRJNAME")
	endpoint := sysconfig.Config.Aliyun_Oss.Endpoint //lego.GetIniByKey("ALIYUN-OSS", "ENDPOINT")
	accessKeyId := sysconfig.Config.Aliyun_Oss.Key_Id //lego.GetIniByKey("ALIYUN-OSS", "KEYID")
	accessKeySecret := sysconfig.Config.Aliyun_Oss.Key_Secret //lego.GetIniByKey("ALIYUN-OSS", "KEYSECRET")
	bucketName := sysconfig.Config.Aliyun_Oss.Bucket //lego.GetIniByKey("ALIYUN-OSS", "BUCKET")
	objectName := prjname + "/" + filename

	// 创建OSSClient实例。
	client, err := oss.New(endpoint, accessKeyId, accessKeySecret)
	if err != nil {
		lego.LogError(err)
		ctx.SetStatusCode(500)
		return
	}

	// 获取存储空间。
	bucket, err := client.Bucket(bucketName)
	if err != nil {
		lego.LogError(err)
		ctx.SetStatusCode(500)
		return
	}

	//localFileName = strings.ReplaceAll(localFileName,"\\", "/")
	objectName = strings.ReplaceAll(objectName,"\\", "/")
	// 上传文件。
	err = bucket.PutObject(objectName, fh) //err = bucket.PutObjectFromFile(objectName, localFileName)
	if err != nil {
		lego.LogError(err)
		ctx.SetStatusCode(500)
		return
	}

	// 一切ok，返回数据
	reponse := make(map[string]interface{})

	code := 0
	msg := "成功"

	reponse["errno"] = code
	reponse["msg"] = msg


	finalPath := "http://" + bucketName + "." + endpoint + "/" + objectName
	reponse["url"] = finalPath
	reponse["state"] = "SUCCESS"

	ctxExt := lego.RequestCtxExtent{ctx}
	ctxExt.JSON(200, reponse)
	return
}

/** 百度图文Editor处理文件上传
 * params
 */
func (vc *UploadCtrl) PostEditorUpload(ctx *fasthttp.RequestCtx) {

	// 这里直接获取到 multipart.FileHeader, 需要手动打开文件句柄
	f, err := ctx.FormFile("upfile")
	if err != nil {
		ctx.SetStatusCode(500)
		lego.LogError(err)
		return
	}
	fh, err := f.Open()
	if err != nil {
		lego.LogError(err)
		ctx.SetStatusCode(500)
		return
	}
	defer fh.Close()

	// 按照时间保存
	now := time.Now().Format("20060102")
	timestamp := string(utils.RandString(32, 3))
	saveDir := filepath.Join(vc.savePath, path.Clean("/"+now))
	if exist, _ := utils.PathExists(saveDir); !exist {
		mkdirerr := os.Mkdir(saveDir, os.ModePerm)
		if mkdirerr != nil {
			ctx.SetStatusCode(500)
			lego.LogError(err)
			return
		}
	}

	// 保存路径
	savePath := path.Clean("/" + now + "/" + timestamp + path.Ext(f.Filename))
	fmt.Println("savePath=" + savePath)
	// 打开保存文件句柄
	fileName := filepath.Join(vc.savePath, savePath)
	fp, err := os.OpenFile(fileName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0666)
	if err != nil {
		ctx.SetStatusCode(500)
		lego.LogError(err)
		return
	}
	defer fp.Close()

	if _, err = io.Copy(fp, fh); err != nil {
		lego.LogError(err)
		ctx.SetStatusCode(500)
		return
	}

	// 一切ok，返回数据
	domain := lego.GetIniByKey("HTTP", "DOMAIN")

	reponse := make(map[string]interface{})

	code := 0
	msg := "成功"

	reponse["errno"] = code
	reponse["msg"] = msg


	finalPath := domain + "/static" + savePath
	reponse["url"] = finalPath
	reponse["state"] = "SUCCESS"

	ctxExt := lego.RequestCtxExtent{ctx}
	ctxExt.JSON(200, reponse)
	return
}


func (vc *UploadCtrl) GetEditorUpload(ctx *fasthttp.RequestCtx) {
	ctxExt := lego.RequestCtxExtent{ctx}
	filename := "./static/vendors/ueditor/go/config.json"
	ctxExt.SendFile(filename)
	return
}


func (vc *UploadCtrl) GetEditorconfig(ctx *fasthttp.RequestCtx) {
	ctxExt := lego.RequestCtxExtent{ctx}
	filename := "./upload/ueditor.config.js"
	ctxExt.SendFile(filename)
	return
}